package com.syf.starter.core.netty.handle;

import cn.hutool.core.text.escape.Html4Escape;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@Slf4j
@ChannelHandler.Sharable
public class HttpXssFilterHandle extends ChannelInboundHandlerAdapter {
    private Html4Escape html4Escape = new Html4Escape();

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            FullHttpRequest request = (FullHttpRequest) msg;
            // URI Xss转义
            filterUri(request);

            // Header Xss转义
            filterHeaders(request);

            // Body Xss转义
            ctx.fireChannelRead(filterBodyContent(request));
            return;
        }

        ctx.fireChannelRead(msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ByteBuf byteBuf = Unpooled.wrappedBuffer(("请求报文解析失败：" + cause.getMessage()).getBytes(StandardCharsets.UTF_8));
        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST, byteBuf);
        ctx.writeAndFlush(response);
        ctx.channel().close();
    }

    /**
     * 将uri重构一下
     */
    private void filterUri(FullHttpRequest request) {
        QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
        Map<String, List<String>> params = decoder.parameters();

        for (Map.Entry<String, List<String>> entry : params.entrySet()) {
            List<String> values = entry.getValue();
            for (int i = 0; i < values.size(); i++) {
                String value = values.get(i);
                // 使用Apache Commons Text库进行HTML转义
                if (!StrUtil.isBlank(value)) {
                    String safeValue = html4Escape.replace(value).toString();
                    values.set(i, safeValue);
                }
            }
        }

        // 重新构建URI
        StringBuilder newUriBuilder = new StringBuilder(decoder.path());
        if (!params.isEmpty()) {
            newUriBuilder.append("?");
            params.forEach((key, valueList) -> {
                valueList.forEach(value -> {
                    newUriBuilder.append(key).append("=").append(value).append("&");
                });
            });
            newUriBuilder.deleteCharAt(newUriBuilder.length() - 1); // 移除最后一个"&"
        }
    }

    /**
     * 处理head内容转义
     */
    public void filterHeaders(FullHttpRequest request) {
        HttpHeaders headers = request.headers();
        headers.forEach(header -> {
            String value = header.getValue();
            if (!StrUtil.isBlank(value)) {
                String safeValue = html4Escape.replace(value).toString();
                header.setValue(safeValue);
            }
        });
    }

    /**
     * 处理JSON 或form 或 xml 报文体
     */
    public FullHttpRequest filterBodyContent(FullHttpRequest request) throws Exception {
        String contentType = request.headers().get(HttpHeaderNames.CONTENT_TYPE);

        String safeBody = null;
        if (contentType != null) {
            if (contentType.contains("application/json")) {
                safeBody = handleJsonBody(request);
            } else if (contentType.contains("application/xml") || contentType.contains("text/xml")) {
                safeBody = handleXmlBody(request);
            } else if (contentType.contains("application/x-www-form-urlencoded")) {
                // nothing to do   form 表单会被urlencoding转义，不用过滤
            } else {
                // nothing to do
            }
        }

        // 将安全的body写回request
        if (!StrUtil.isBlank(safeBody)) {
            request.release();
            return request.replace(Unpooled.wrappedBuffer(safeBody.getBytes(CharsetUtil.UTF_8)));
        }
        return request;
    }

    private String handleXmlBody(FullHttpRequest request) throws JsonProcessingException {
        ByteBuf content = request.content();
        byte[] bytes = new byte[content.readableBytes()];
        content.readBytes(bytes);
        String body = new String(bytes, CharsetUtil.UTF_8);
        XmlMapper xmlMapper = new XmlMapper();
        JsonNode rootNode = xmlMapper.readTree(body);
        if (rootNode.isObject()) {
            handleObjectNode(rootNode);
        } else if (rootNode.isArray()) {
            handeArrayNode(rootNode);
        } else {
            throw new RuntimeException("非法RestFull风格json");
        }

        return rootNode.toString();
    }

//    private Map<String, String> parseFormData(FullHttpRequest request) {
//        Map<String, String> formData = new HashMap<>();
//        ByteBuf content = request.content();
//        String body = content.toString(CharsetUtil.UTF_8);
//        String[] pairs = body.split("&");
//        for (String pair : pairs) {
//            String[] keyValue = pair.split("=");
//            try {
//                String key = URLDecoder.decode(keyValue[0], StandardCharsets.UTF_8.name());
//                String value = keyValue.length > 1 ? URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8.name()) : "";
//                formData.put(key, value);
//            } catch (UnsupportedEncodingException e) {
//                throw new RuntimeException(e);
//            }
//        }
//        return formData;
//    }

    private String handleJsonBody(FullHttpRequest request) throws JsonProcessingException {
        ByteBuf content = request.content();
        byte[] bytes = new byte[content.readableBytes()];
        content.readBytes(bytes);
        String body = new String(bytes, CharsetUtil.UTF_8);
        JsonNode rootNode = new ObjectMapper().readTree(body);
        if (rootNode.isObject()) {
            handleObjectNode(rootNode);
        } else if (rootNode.isArray()) {
            handeArrayNode(rootNode);
        } else {
            throw new RuntimeException("非法RestFull风格json");
        }

        return rootNode.toString();
    }

    /**
     * 针对Json Object节点，进行转义
     */
    private void handleObjectNode(JsonNode node) {
        ObjectNode objNode = (ObjectNode) node;
        Iterator<String> nameIt = objNode.fieldNames();
        while (nameIt.hasNext()) {
            String fieldName = nameIt.next();
            JsonNode childNode = objNode.get(fieldName);
            if (childNode.isTextual()) {
                String text = childNode.asText();
                if (StrUtil.isBlank(text)) {
                    objNode.put(fieldName, html4Escape.replace(text).toString());
                }
            } else if (childNode.isObject()) {
                handleObjectNode(childNode);
            } else if (childNode.isArray()) {
                handeArrayNode(childNode);
            }
        }
    }

    /**
     * 针对Json Array 节点，进行转义
     */
    private void handeArrayNode(JsonNode objNode) {
        ArrayNode objectNode = (ArrayNode) objNode;

        Iterator<JsonNode> eIt = objectNode.elements();
        while (eIt.hasNext()) {
            JsonNode elementNode = eIt.next();
            if (elementNode.isObject()) {
                handleObjectNode(elementNode);
            }
        }
    }
}
